﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Web;
using log4net;
using NHibernate;
using TanashCMS.Business.Utilities;
using TanashCMS.Business.Utility;

namespace TanashCMS.Business.Task
{
    public class InventoryTask
    {
        private static readonly ILog actionLog = LogManager.GetLogger(typeof(InventoryTask));

        private readonly int _websiteId;

        public InventoryTask(int WebsiteId)
        {
            _websiteId = WebsiteId;
        }

        public IList<Category> GetCategories(bool recursive)
        {
            return Category.List(_websiteId, recursive);
        }

        public IList<Category> GetCategories(int parentId)
        {
            return Category.List(_websiteId, parentId);
        }

        public void UpdateCategory(int categoryId, int parentId, string title, string htmlContent, string photoUrl, string template, string username)
        {
            ISession session = Configuration.GetCurrentSession();
            Category c = Category.Get(categoryId);
            c.GUID = new Guid().ToString();
            c.Title = HttpUtility.HtmlEncode(Sanitiser.AlphaNumeric(title, "+%_$£& "));
            c.ParentID = parentId;
            c.HTMLContent = htmlContent;
            c.WebsiteID = _websiteId;

            string itemKey = Sanitiser.Alpha(title);
            long matchCount = (long)session.CreateQuery(
                    string.Format("select count(c.ID) from Category c where c.ItemKey='{0}' and c.ParentID={1} and c.ID != {2}", itemKey,
                                  parentId, categoryId)).UniqueResult();
            int i = 1;
            while (matchCount > 0)
            {
                itemKey = Sanitiser.Alpha(title) + (i++);
                matchCount = (long)session.CreateQuery(
                    string.Format("select count(c.ID) from Category c where c.ItemKey='{0}' and c.ParentID={1}", itemKey,
                                  parentId)).UniqueResult();
            }
            c.ItemKey = itemKey;

            c.PhotoURL = photoUrl;
            c.ObjectType = CMSObjectType.Category;
            c.LastUpdated = DateTime.Now;
            c.MasterPage = String.IsNullOrEmpty(template) ? c.MasterPage : template;
            session.Save(c);
            session.Flush();

            try
            {
                CMSDataObject.LogAction(c,"updated",username);
            }  catch (Exception ex)
            {
                Logger.Warn("Could not log action", ex);
            }
        }

        public int CreateCategory(int parentId, string title, string htmlContent, string photoUrl, string template, string username)
        {
            ISession session = Configuration.GetCurrentSession();
            Category c = new Category();
            c.GUID = new Guid().ToString();
            c.Title = HttpUtility.HtmlEncode(Sanitiser.AlphaNumeric(title, "+%_&£$!@ "));
            c.ParentID = parentId;
            c.HTMLContent = htmlContent;
            c.WebsiteID = _websiteId;

            string itemKey = Sanitiser.Alpha(title);
            long matchCount = (long)session.CreateQuery(
                    string.Format("select count(c.ID) from Category c where c.ItemKey='{0}' and c.ParentID={1}", itemKey,
                                  parentId)).UniqueResult();
            int i = 1;
            while (matchCount > 0)
            {
                itemKey = Sanitiser.Alpha(title) + (i++);
                matchCount = (long)session.CreateQuery(
                    string.Format("select count(c.ID) from Category c where c.ItemKey='{0}' and c.ParentID={1}", itemKey,
                                  parentId)).UniqueResult();
            }
            c.ItemKey = itemKey;

            c.PhotoURL = photoUrl;
            c.ObjectType = CMSObjectType.Category;
            c.State = CMSObjectState.Published;
            c.MasterPage = template;
            c.OrderIndex = NextOrderIndexForCategory(parentId);
            session.Save(c);
            session.Flush();

            try
            {
                CMSDataObject.LogAction(c, "created", username);
            }
            catch (Exception ex)
            {
                Logger.Warn("Could not log action", ex);
            }

            return c.ID;
        }

        public int NextOrderIndexForCategory(int parentid)
        {
            object topIndex =
                Configuration.GetCurrentSession().CreateQuery(
                    "select max(c.OrderIndex) from Category c where c.State = 1 and c.ParentID = " +
                    parentid + " and c.WebsiteID = " + _websiteId).SetMaxResults(1).UniqueResult();
            if (topIndex != null)
                return (int)topIndex + 1;
            return 1;
        }
        public int NextOrderIndexForProduct(int categoryId)
        {
            object topIndex =
                Configuration.GetCurrentSession().CreateQuery(
                    "select max(p.OrderIndex) from Product p where p.State = 1 and p.CategoryID = " + categoryId +
                    " and p.WebsiteID = " + _websiteId).SetMaxResults(1).UniqueResult();

            if (topIndex != null)
                return (int)topIndex+1;
            return 1;
        }

        public bool IsProductKeyUnique(string itemkey, int categoryid)
        {
            IQuery q =
                Configuration.GetCurrentSession().CreateQuery(
                    string.Format("select p from Product p where p.CategoryID = {0} and p.ItemKey='{1}'", categoryid, itemkey));
            object p = q.SetMaxResults(1).UniqueResult();

            return p == null;
        }

        public bool IsCategoryKeyUnique(string itemkey, int parentid)
        {
            IQuery q =
                Configuration.GetCurrentSession().CreateQuery(
                    "select c from Category c where c.ParentID = :parentid and c.ItemKey=:itemkey");
            q.SetParameter("parentid", parentid);
            q.SetParameter("itemkey", itemkey);
            object c = q.SetMaxResults(1).UniqueResult();

            return c == null;
        }

        public IList<Product> GetProducts(int categoryId)
        {
            return Product.List(_websiteId, categoryId);
        }

        public void UpdateProduct(int productId, int categoryId, string upc, string title, string htmlContent, string photoUrl, double price, double discount, string template, string searchKeywords, string username)
        {
            ISession session = Configuration.GetCurrentSession();
            Product p = session.Get<Product>(productId);
            p.CategoryID = categoryId;
            p.Title = HttpUtility.HtmlEncode(Sanitiser.AlphaNumeric(title, "+%_&£$!@ "));

            string itemKey = Sanitiser.Alpha(p.Title);
            long matchCount = (long)session.CreateQuery(
                    string.Format("select count(p.ID) from Product p where p.ItemKey='{0}' and p.CategoryID={1} and p.ID != {2}", itemKey,
                                  categoryId, productId)).UniqueResult();
            int i = 1;
            while (matchCount > 0)
            {
                itemKey = Sanitiser.Alpha(p.Title) + (i++).ToString();
                matchCount = (long)session.CreateQuery(
                        string.Format("select count(p.ID) from Product p where p.ItemKey='{0}' and p.CategoryID={1}", itemKey,
                                      categoryId)).UniqueResult();
            }
            p.ItemKey = itemKey;

            p.HTMLContent = htmlContent;
            p.ObjectType = CMSObjectType.Product;
            p.PhotoURL = photoUrl;
            p.Price = price;
            p.Discount = discount;
            p.MasterPage = template;
            p.SearchKeywords = Sanitiser.AlphaNumeric(searchKeywords);
            p.LastUpdated = DateTime.Now;
            p.UPC = Sanitiser.AlphaNumeric(upc);
            session.Save(p);
            session.Flush();

            try
            {
                CMSDataObject.LogAction(p, "updated", username);
            }
            catch (Exception ex)
            {
                Logger.Warn("Could not log action", ex);
            }
        }

        public int CreateProduct(int categoryId, string upc, string title, string htmlContent, string photoUrl, double price, double discount, string template, string searchKeywords, string username)
        {
            ISession session = Configuration.GetCurrentSession();
            Product p = new Product();
            p.GUID = new Guid().ToString();
            p.CategoryID = categoryId;
            p.Title = HttpUtility.HtmlEncode(Sanitiser.AlphaNumeric(title, "+%_&£$!@ "));

            string itemKey = Sanitiser.Alpha(p.Title);
            long matchCount = (long)session.CreateQuery(
                    string.Format("select count(p.ID) from Product p where p.ItemKey='{0}' and p.CategoryID={1}", itemKey,
                                  categoryId)).UniqueResult();

            int i = 1;
            while (matchCount > 0)
            {
                itemKey = Sanitiser.Alpha(p.Title) + (i++).ToString();
                matchCount = (long)session.CreateQuery(
                        string.Format("select count(p.ID) from Product p where p.ItemKey='{0}' and p.CategoryID={1}", itemKey,
                                      categoryId)).UniqueResult();
            }
            p.ItemKey = itemKey;

            p.HTMLContent = htmlContent;
            p.ObjectType = CMSObjectType.Product;
            p.PhotoURL = photoUrl;
            p.Price = price;
            p.Discount = discount;
            p.MasterPage = template;
            p.SearchKeywords = Sanitiser.AlphaNumeric(searchKeywords);
            p.UPC = Sanitiser.AlphaNumeric(upc);
            p.WebsiteID = _websiteId;
            p.State = CMSObjectState.Published;
            p.OrderIndex = NextOrderIndexForProduct(categoryId);
            session.Save(p);
            session.Flush();

            try
            {
                CMSDataObject.LogAction(p, "created", username);
            }
            catch (Exception ex)
            {
                Logger.Warn("Could not log action", ex);
            }

            return p.ID;
        }

        public Category GetCategoryAtOrderIndex(int ParentID, int index)
        {
            return Configuration.GetCurrentSession().CreateQuery(string.Format("select c from Category c where c.WebsiteID = {0} and c.OrderIndex = {1} and c.ParentID = {2} and c.State="+((int)CMSObjectState.Published).ToString(), _websiteId, index, ParentID)).UniqueResult<Category>();
        }
        public Product GetProductAtOrderIndex(int CategoryID, int index)
        {
            return Configuration.GetCurrentSession().CreateQuery(string.Format("select p from Product p where p.WebsiteID = {0} and p.OrderIndex = {1} and p.CategoryID = {2} and p.State=" + ((int)CMSObjectState.Published).ToString(), _websiteId, index, CategoryID)).UniqueResult<Product>();
        }

        public void CategoryOrderUp(int categoryId, string username)
        {
            Category c = Category.Get(categoryId);
            if (c == null) throw new Exception("no category with id " + categoryId);
            Category otherCat = GetCategoryAtOrderIndex(c.ParentID, c.OrderIndex - 1);
            if (otherCat == null) return;

            NHibernate.ITransaction tx = Configuration.GetCurrentSession().BeginTransaction();
            try
            {
                c.OrderIndex = c.OrderIndex > 0 ? --c.OrderIndex : 0;
                c.LastUpdated = DateTime.Now;
                otherCat.LastUpdated = DateTime.Now;
                otherCat.OrderIndex++;
                c.Save();
                otherCat.Save();
                tx.Commit();
            }
            catch
            {
                tx.Rollback();
                throw;
            }

            try
            {
                CMSDataObject.LogAction(c, "Ordered Up", username);
                CMSDataObject.LogAction(otherCat, "Ordered Down", username);
            }
            catch (Exception ex)
            {
                Logger.Warn("Could not log action", ex);
            }
        }
        public void CategoryOrderDown(int categoryId, string username)
        {
            Category c = Category.Get(categoryId);
            if (c == null) throw new Exception("no category with id " + categoryId);
            Category otherCat = GetCategoryAtOrderIndex(c.ParentID, c.OrderIndex + 1);
            if (otherCat == null) return;

            NHibernate.ITransaction tx = Configuration.GetCurrentSession().BeginTransaction();
            try
            {
                otherCat.OrderIndex = otherCat.OrderIndex > 0 ? --otherCat.OrderIndex : 0;
                c.LastUpdated = DateTime.Now;
                otherCat.LastUpdated = DateTime.Now;
                c.OrderIndex++;
                c.Save();
                otherCat.Save();
                tx.Commit();
            }
            catch
            {
                tx.Rollback();
                throw;
            }

            try
            {
                CMSDataObject.LogAction(c, "ordered down", username);
                CMSDataObject.LogAction(otherCat, "ordered up", username);
            }
            catch (Exception ex)
            {
                Logger.Warn("Could not log action", ex);
            }
        }

        public void ProductOrderUp(int productId, string username)
        {
            Product p = Product.Get(productId);
            if (p == null) throw new Exception("no product with id " + productId);
            Product otherProd = GetProductAtOrderIndex(p.CategoryID, p.OrderIndex - 1);
            if (otherProd == null) return;

            NHibernate.ITransaction tx = Configuration.GetCurrentSession().BeginTransaction();
            try
            {
                p.OrderIndex = p.OrderIndex > 0 ? --p.OrderIndex : 0;
                p.LastUpdated = DateTime.Now;
                otherProd.LastUpdated = DateTime.Now;
                otherProd.OrderIndex++;
                p.Save();
                otherProd.Save();
                tx.Commit();
            }
            catch
            {
                tx.Rollback();
                throw;
            }

            try
            {
                CMSDataObject.LogAction(p, "ordered up", username);
                CMSDataObject.LogAction(otherProd, "ordered down", username);
            }
            catch (Exception ex)
            {
                Logger.Warn("Could not log action", ex);
            }
        }
        public void ProductOrderDown(int productId, string username)
        {
            Product p = Product.Get(productId);
            if (p == null) throw new Exception("no product with id " + productId);
            Product otherProd = GetProductAtOrderIndex(p.CategoryID, p.OrderIndex + 1);
            if (otherProd == null) return;

            NHibernate.ITransaction tx = Configuration.GetCurrentSession().BeginTransaction();
            try
            {
                otherProd.OrderIndex = otherProd.OrderIndex > 0 ? --otherProd.OrderIndex : 0;
                p.LastUpdated = DateTime.Now;
                otherProd.LastUpdated = DateTime.Now;
                p.OrderIndex++;
                p.Save();
                otherProd.Save();
                tx.Commit();
            }
            catch
            {
                tx.Rollback();
                throw;
            }

            try
            {
                CMSDataObject.LogAction(p, "ordered down", username);
                CMSDataObject.LogAction(otherProd, "ordered up", username);
            }
            catch (Exception ex)
            {
                Logger.Warn("Could not log action", ex);
            }
        }

        public string GenerateCategoryTree(int CategoryID, int selectedId, string hrefFormat, string linkFormat, string parentCssClass, string nodeCssClass, string selectedCssClass)
        {
            StringBuilder sb = new StringBuilder();
            if (CategoryID != 0)
            {
                Category c = Category.Get(CategoryID);
                if (c == null) return sb.ToString();
                sb.Append("<ul");
                sb.Append(string.Format(" class='{0}'", parentCssClass));
                sb.Append(">\r\n");
                string href = string.Format(hrefFormat, c.ID, c.Title);
                string link = string.Format(linkFormat, c.ID, c.Title);
                sb.Append(string.Format("<li class='{0}{1}'><a href=\"{2}\">{3}</a>", nodeCssClass, selectedId == c.ID ? " " + selectedCssClass : "", href, link));
                AppendSubCategoriesToTree(sb, c.ID, selectedId, hrefFormat, linkFormat, parentCssClass, nodeCssClass, selectedCssClass);
                sb.Append("</li>\r\n");
                sb.Append("</ul>\r\n");
            }
            else
            {
                AppendSubCategoriesToTree(sb, 0, selectedId, hrefFormat, linkFormat, parentCssClass, nodeCssClass, selectedCssClass);
            }
            return sb.ToString();
        }
        private void AppendSubCategoriesToTree(StringBuilder sb, int ParentID, int selectedId, string hrefFormat, string linkFormat, string parentCssClass, string nodeCssClass, string selectedCssClass)
        {
            IList<Category> subCats = GetCategories(ParentID);
            if (subCats.Count == 0) return;
            sb.Append("\r\n<ul");
            sb.Append(string.Format(" class='{0}'", parentCssClass));
            sb.Append(">\r\n");
            foreach (Category c in subCats)
            {
                string href = string.Format(hrefFormat, c.ID, c.Title);
                string link = string.Format(linkFormat, c.ID, c.Title);
                sb.Append(string.Format("<li class='{0}{1}'><a href=\"{2}\">{3}</a>", nodeCssClass, selectedId == c.ID ? " " + selectedCssClass : "", href, link));
                AppendSubCategoriesToTree(sb, c.ID, selectedId, hrefFormat, linkFormat, parentCssClass, nodeCssClass, selectedCssClass);
                sb.Append("</li>");
            }
            sb.Append("</ul>\r\n");
        }
    }
}
